package com.imooc.bilibili.api;

import com.alibaba.fastjson.JSONObject;
import com.imooc.bilibili.domain.JsonResponse;
import com.imooc.bilibili.domain.PageResult;
import com.imooc.bilibili.domain.User;
import com.imooc.bilibili.domain.UserInfo;
import com.imooc.bilibili.service.UserFollowingService;
import com.imooc.bilibili.service.UserService;
import com.imooc.bilibili.support.UserSupport;
import com.imooc.bilibili.util.RSAUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

/**
 * @author xiexu
 * @create 2022-06-14 19:16
 */
@RestController
public class UserApi {

    @Autowired
    private UserService userService;

    @Autowired
    private UserSupport userSupport;

    @Autowired
    private UserFollowingService userFollowingService;

    /**
     * 获取RSA公钥
     *
     * @return
     */
    @GetMapping("/rsa-pks")
    public JsonResponse<String> getRsaPublicKey() {
        String pk = RSAUtil.getPublicKeyStr();
        // 返回RSA公钥数据给前端
        return new JsonResponse<>(pk);
    }

    /**
     * 用户注册
     * "@RequestBody"：将前端发送过来的请求体中的JSON字符串绑定到对应的bean上
     *
     * @param user
     * @return
     */
    @PostMapping("/users")
    public JsonResponse<String> addUser(@RequestBody User user) {
        userService.addUser(user);
        return JsonResponse.success();
    }

    /**
     * 用户登录
     * 返回登录token给前端
     *
     * @param user
     * @return
     */
    @PostMapping("/user-tokens")
    public JsonResponse<String> login(@RequestBody User user) throws Exception {
        String token = userService.login(user);
        return new JsonResponse<>(token);
    }

    /**
     * 获取用户信息
     * 前端不用传任何参数，后端可以在前端请求的请求头里面拿到token，并解析成userId
     *
     * @return
     */
    @GetMapping("/users")
    public JsonResponse<User> getUserInfo() {
        // 从浏览器请求的请求头中抓取token，进行解析从而拿到userId
        Long userId = userSupport.getCurrentUserId();
        // 通过用户id获取用户相关信息
        User user = userService.getUserInfo(userId);
        return new JsonResponse<>(user);
    }

    /**
     * 更新用户信息
     * 注意：userId一般是通过token进行获取的，而不是前端直接传给后端的；
     * 因为如果userId直接从前端传到后端，可能会被拦截，然后仿造一个相同的用户id或其他用户id，
     * 被攻击者来利用我们的接口获取用户的信息，所以这是不安全的！！！
     * 而token是有过期时间的，这样子安全性会更高，这点需要注意！！！
     *
     * @param user
     * @return
     */
    @PutMapping("/users")
    public JsonResponse<String> updateUsers(@RequestBody User user) throws Exception {
        // 从浏览器请求的请求头中抓取token，进行解析从而拿到userId
        Long userId = userSupport.getCurrentUserId();
        user.setId(userId);
        userService.updateUsers(user);
        return JsonResponse.success();
    }

    @PutMapping("/user-infos")
    public JsonResponse<String> updateUserInfos(@RequestBody UserInfo userInfo) {
        // 从浏览器请求的请求头中抓取token，进行解析从而拿到userId
        Long userId = userSupport.getCurrentUserId();
        userInfo.setUserId(userId);
        userService.updateUserInfos(userInfo);
        return JsonResponse.success();
    }

    /**
     * @param no   当前页码
     * @param size 每页显示多少条数据
     * @param nick 根据昵称进行模糊查询
     * @return
     * @RequestParam 表示前端必须把该参数传给后端接口
     */
    @GetMapping("/user-infos")
    public JsonResponse<PageResult<UserInfo>> pageListUserInfos(@RequestParam Integer no, @RequestParam Integer size, String nick) {
        // 从浏览器请求的请求头中抓取token，进行解析从而拿到userId
        Long userId = userSupport.getCurrentUserId();
        // JSONObject 其实相当于Map类，可以当成map类来使用
        JSONObject params = new JSONObject();
        params.put("no", no);
        params.put("size", size);
        params.put("nick", nick);
        params.put("userId", userId);
        PageResult<UserInfo> result = userService.pageListUserInfos(params);
        if (result.getTotal() > 0) {
            // 检查当前用户的关注状态
            List<UserInfo> checkedUserInfoList = userFollowingService.checkFollowingStatus(result.getList(), userId);
            result.setList(checkedUserInfoList);
        }
        return new JsonResponse<>(result);
    }

    /**
     * 用户登录
     * 双token机制：accessToken、refreshToken
     * dts的含义：double token（双token）
     *
     * @param user
     * @return
     */
    @PostMapping("/user-dts")
    public JsonResponse<Map<String, Object>> loginForDts(@RequestBody User user) throws Exception {
        Map<String, Object> map = userService.loginForDts(user);
        return new JsonResponse<>(map);
    }

    /**
     * 退出登录
     * 用户退出登录以后，需要把现有的刷新token删除掉
     *
     * @param request 包含所有请求信息的集合
     * @return
     */
    @DeleteMapping("/refresh-tokens")
    public JsonResponse<String> logout(HttpServletRequest request) {
        // 通过HttpServletRequest获取请求头中的refreshToken
        String refreshToken = request.getHeader("refreshToken");
        // 获取userId
        Long userId = userSupport.getCurrentUserId();
        userService.logout(refreshToken, userId);
        return JsonResponse.success();
    }

    /**
     * 刷新accessToken(访问token)
     * 生成一个新的访问token给前端
     * 具体流程就是：用户携带访问token访问服务器，当用户的访问token过期时，服务端会返回异常给前端，
     * 这时候前端识别到该异常为访问token过期，先不抛出提示信息给用户，而是先调用刷新accessToken的接口，
     * 传入刷新token给服务器验证，如果服务器验证刷新token没有失效，就重新生成一个访问token给用户，
     * 这样子用户就可以无感的刷新访问token，而不会出现烦人的提示信息；如果服务器验证刷新token已经失效了，
     * 则抛出提示信息给用户（当前用户已经退出登录，请重新登录！）
     *
     * @param request
     * @return
     */
    @PostMapping("/access-tokens")
    public JsonResponse<String> refreshAccessToken(HttpServletRequest request) throws Exception {
        // 先从请求头中获取刷新token
        String refreshToken = request.getHeader("refreshToken");
        // 传入刷新token，获取刷新之后的访问token
        String accessToken = userService.refreshAccessToken(refreshToken);
        // 返回一个新的访问token给前端
        return new JsonResponse<>(accessToken);
    }

}
